package com.huayu.languo.common.util;

import com.huayu.languo.model.dto.ErrorFild;
import com.huayu.languo.model.enums.LogLevel;
import com.huayu.languo.model.enums.ResultCode;
import com.huayu.languo.model.excp.AbstractAppException;
import com.huayu.languo.model.excp.BizException;
import com.huayu.languo.model.excp.SystemException;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.internal.engine.path.NodeImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Path;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author 刘雄康
 * @version v1.0
 * @description 异常工具类
 * @date 2019年05月14日
 */
public final class ExceptionUtil {

    private static final Logger LOG = LoggerFactory.getLogger(ExceptionUtil.class);

    private ExceptionUtil() {
        // Do nothing
    }

    public static boolean mustBetween(Integer body, int min, int max) {
        boolean flag = false;
        if (null != body && body >= min && body <= max) {
            flag = true;
        }
        return flag;
    }

    public static boolean mustShortThan(String body, int length) {
        boolean flag = false;
        if (StringUtils.isNotEmpty(body) && body.length() <= length) {
            flag = true;
        }
        return flag;
    }

    public static boolean shouldShortThan(String body, int length) {
        boolean flag = false;
        if (StringUtils.isEmpty(body) || body.length() <= length) {
            flag = true;
        }
        return flag;
    }

    public static void throwExp(AbstractAppException ex) {
        if (null != ex) {
            throw ex;
        }
    }

    public static void throwBizExp(ResultCode resultCode, String msg) {
        throwBizExp(resultCode, null, msg);
    }

    public static void throwBizExp(ResultCode resultCode, Object data, String msg) {
        throwExp(new BizException(resultCode, data, msg));
    }

    public static void notNull(Object obj, AbstractAppException ex) {
        if (null == obj) {
            throw ex;
        }
    }

    public static void notEmpty(Collection collection, AbstractAppException ex) {
        if (null == collection || collection.isEmpty()) {
            throw ex;
        }
    }

    public static void notEmpty(Map<String, String> map, AbstractAppException ex) {
        if (null == map || map.isEmpty()) {
            throw ex;
        }
    }

    public static void notEmpty(String data, AbstractAppException ex) {
        if (StringUtils.isEmpty(data)) {
            throw ex;
        }
    }

    public static void notTrue(Boolean flag, AbstractAppException ex) {
        if (null != flag && flag) {
            throw ex;
        }
    }

    public static void notFalse(Boolean flag, AbstractAppException ex) {
        if (null == flag || !flag) {
            throw ex;
        }
    }

    public static boolean isHttpBindExp(Exception e) {
        boolean flag;
        if (e instanceof ServletRequestBindingException || e instanceof HttpRequestMethodNotSupportedException || e instanceof HttpMediaTypeException) {
            flag = true;
        } else if (e instanceof HttpMessageNotReadableException) {
            flag = true;
        } else if (e instanceof MethodArgumentTypeMismatchException || e instanceof MethodArgumentConversionNotSupportedException) {
            flag = true;
        } else if (e instanceof MethodArgumentNotValidException || e instanceof ConstraintViolationException) {
            flag = true;
        } else {
            flag = false;
        }
        return flag;
    }

    public static void logExp(LogLevel logLevel, String logPattern, Object[] getLogParams) {
        if (logLevel == LogLevel.DEBUG) {
            LOG.debug(logPattern, getLogParams);
        } else if (logLevel == LogLevel.INFO) {
            LOG.info(logPattern, getLogParams);
        } else if (logLevel == LogLevel.WARN) {
            LOG.warn(logPattern, getLogParams);
        } else {
            LOG.error(logPattern, getLogParams);
        }
    }

    public static String getValidateMsg(Exception e) {
        try {
            String validateMsg;
            if (e instanceof MethodArgumentNotValidException) {
                validateMsg = getMAValidMsg((MethodArgumentNotValidException) e);
            } else if (e instanceof ConstraintViolationException) {
                validateMsg = getConstraintViolationMsg((ConstraintViolationException) e);
            } else {
                validateMsg = "请检查http请求 头定义、方法类型、参数缺失等是否符合定义";
            }
            return validateMsg;
        } catch (Exception e1) {
            LOG.error(e1.getMessage(), e1);
        }
        return "错误信息解析失败！";
    }

    private static String getConstraintViolationMsg(ConstraintViolationException e) {
        String validateMsg;
        ConstraintViolationException ex = e;
        List<ErrorFild> errorFields = new ArrayList<>();
        Iterator<ConstraintViolation<?>> iterator = ex.getConstraintViolations().iterator();
        while (iterator.hasNext()) {
            ConstraintViolation<?> next = iterator.next();
            Iterator<Path.Node> nodeIterator = next.getPropertyPath().iterator();
            Integer paramIndex = 0;
            String methodName = "";
            String kindName = "";
            List<Class<?>> parameterTypes = new ArrayList<>();
            int i = 0;
            while (nodeIterator.hasNext()) {
                Path.Node node = nodeIterator.next();
                if (i == 0) {
                    methodName = node.getName();
                    parameterTypes = ((NodeImpl) node).getParameterTypes();
                } else if (i == 1) {
                    kindName = node.getKind().name();
                    if ("PARAMETER".equals(kindName)) {
                        paramIndex = ((NodeImpl) node).getParameterIndex();
                    }
                } else {
                    break;
                }
                i++;
            }
            Object invalidValue = next.getInvalidValue();
            String message = next.getMessage();
            if ("PARAMETER".equals(kindName)) {
                String[] paramNames = getParamNames(next.getLeafBean().getClass(), methodName, parameterTypes.toArray(new Class<?>[parameterTypes.size()]));
                String theParameName = paramNames[paramIndex];
                errorFields.add(ErrorFild.builder().field(theParameName).message(message).value(invalidValue).build());
            } else {
                errorFields.add(ErrorFild.builder().message(methodName + " 方法返回值异常，" + message).value(invalidValue).build());
            }
        }
        validateMsg = getStrValidateMsg(errorFields);
        return validateMsg;
    }

    private static String getMAValidMsg(MethodArgumentNotValidException e) {
        String validateMsg;
        MethodArgumentNotValidException ex = e;
        List<FieldError> fieldErrorList = ex.getBindingResult().getFieldErrors();
        List<ErrorFild> errorFields = new ArrayList<>();
        for (FieldError fieldError : fieldErrorList) {
            errorFields.add(ErrorFild.builder().field(fieldError.getField()).message(fieldError.getDefaultMessage()).value(fieldError.getRejectedValue()).build());
        }
        validateMsg = getStrValidateMsg(errorFields);
        return validateMsg;
    }

    private static String getStrValidateMsg(List<ErrorFild> errorFilds) {
        String validateMsg;
        List<String> list = new ArrayList<>();
        for (ErrorFild errorFild : errorFilds) {
            list.add(errorFild.toString());
        }
        validateMsg = String.join(" , ", list);
        return validateMsg;
    }

    public static String[] getParamNames(Class<?> clazz, String methodName, Class<?>[] parameTypes) {
        try {
            Method method = clazz.getMethod(methodName, parameTypes);
            ParameterNameDiscoverer pnd = new LocalVariableTableParameterNameDiscoverer();
            return pnd.getParameterNames(method);
        } catch (Exception e) {
            throw new SystemException(e);
        }
    }
}
